require 'rubygems'
require 'net/https'
require 'libxml'
require 'uri'

$api_version = '1.0'

# Class for Mu Api commands. UUID and JobId for most calls are remembered
# but can be overridden when needed.
class Mu_Api
  include LibXML
  
  attr_accessor :host, :docroot, :posted_uuid, :run_uuid, :job_id, :username, :password, :params, :expected_error

  def initialize(host=ENV['MU_IP'], docroot="/api/v3" )
    @host = host
    @username  = ENV['MU_ADMIN_USER'].nil? || "admin"
    @password  = ENV['MU_ADMIN_PASS'].nil? || "admin" 
    @docroot = docroot
    @params = nil
    @expected_error = nil
    $DEBUG and puts "Created API object to :#{@host}"
  end

  # send request to api
  def request(doc,verbose=false)
    uri = URI.parse("https://#{@host}/")
    resp = response = "";
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true
    escaped = URI.escape(doc)
    $DEBUG and puts "DOC: #{@docroot}/#{escaped}"
    http.start do |http|
      req = Net::HTTP::Get.new("#{@docroot}/#{escaped}", {"User-Agent" => @username})
      req.basic_auth(@username, @password)
      response = http.request(req)
      resp = response.body
    end
    begin
      if (/<.+>/).match(resp)
        xmldoc = XML::Document.string(resp)
      else
        xmldoc = XML::Document.new
        xmldoc.root = XML::Node.new('err_node', resp)
      end 
  
      # a test case may set the expected_error string if it expects
      # there to be an error in the http request.
      # if there was an expected error, and we find it in the response,
      # then set it to the 'found' string (which the test case will check for)
      @expected_error = "found expected error" if xmldoc.to_s.include?(@expected_error) unless @expected_error.nil?
  
    rescue SocketError
      raise "Host " + @host + " nicht erreichbar"
    rescue Exception => e
      print "Error parsing XML " + e.to_s
    ensure
      return xmldoc
    end
  end

  # Get the status of a particular analysis, reference is the uuid
  def status(uuid=@run_uuid)
    doc = request("analysis/status?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      status = doc.find_first("//status").content
      return status
    end
    return doc
  end

  # Run an analysis, reference is the posted uuid. A name parameter can
  # be included so that each successive run of the same uuid yields a
  # new name
  # ex: run(1234-1234-1234-1234, "new_name")
  def run(uuid=@posted_uuid, rename="")
    req = "analysis/run?uuid=#{uuid}"
    unless rename.empty?
      req = "#{req}&rename=#{rename}"
    end
    doc = request(req)
    unless doc.find_first("//analysis").nil?
      @run_uuid = doc.find_first("//analysis").attributes['uuid']
      return @run_uuid
    end
    return doc
  end

  # abort a running analysis. Next queued analysis will start
  def stop(uuid=@run_uuid)
    doc = request("analysis/stop?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      status = doc.find_first("//analysis").attributes['status']
      return status
    end
    return false
  end

  # pause a running analysis. Note that any queued analysis will NOT begin
  def pause(uuid=@run_uuid)
    doc = request("analysis/pause?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      status = doc.find_first("//analysis").attributes['status']
      return status
    end
    return false
  end

  # anti-pause
  def resume(uuid=@run_uuid)
    doc = request("analysis/resume?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      status = doc.find_first("//analysis").attributes['status']
      return status
    end
    return false
  end

  # delete analysis or template of any type
  def delete(uuid=@run_uuid)
    doc = request("analysis/delete?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      status = doc.find_first("//analysis").attributes['status']
      return status
    end
    return true
  end

  # return a list of faults (if any) for the analysis
  def get_faults(uuid=@run_uuid)
    doc = request("templates/export?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      faults = request("analysis/getFaultList?uuid=#{uuid}",true)
      return faults
    end
    return "error: no analysis with uuid: #{uuid} found"
  end

  # return the name of an analysis referenced by uuid
  def get_name(uuid=@run_uuid)
    doc = request("templates/export?uuid=#{uuid}")
    unless doc.find_first("//analysis").nil?
      return doc.find_first("//analysis").attributes['name']
    end
    return
  end
  
end
